
/* <<< COPYRIGHT START >>>
 * Copyright 2006-Present OxygenSoftwareLibrary.com
 * Licensed under the GNU Lesser General Public License.
 * http://www.gnu.org/licenses/lgpl.html
 * 
 * @author: Ugorji Dick-Nwoke
 * <<< COPYRIGHT END >>>
 */


package oxygen.tool.wlfacade;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.MBeanInfo;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerInvocationHandler;
import javax.management.ObjectName;
import javax.management.QueryExp;
import javax.management.modelmbean.ModelMBeanInfo;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

import oxygen.tool.facade.FacadeBenignException;
import oxygen.util.StringUtils;
import weblogic.management.MBeanHome;
import weblogic.management.RemoteMBeanServer;

/**
 * Note: All the methods to deal with JMX 1.2 stuff for WLS 9.0 are put here
 */
public class WLScriptHelper {
  public static ObjectName getSingleObjName
    (Object mbs, String domain, String relativeStringRep, WLJMXHandler dmhdlr, 
     ObjectName parentobjname, boolean hasNoParentOnly, QueryExp exp) 
    throws Exception {
    ObjectName[] wlms = getObjNames(mbs, domain, relativeStringRep, dmhdlr, parentobjname, hasNoParentOnly, exp);
    return getSingleObjName(wlms);
  }
  
  public static ObjectName[] getObjNames
    (Object mbs, String domain, String relativeStringRep, WLJMXHandler dmhdlr, 
     ObjectName parentobjname, boolean hasNoParentOnly, QueryExp exp) 
    throws Exception {
    ObjectName pattern = dmhdlr.getLookupPattern(domain, relativeStringRep);
    ObjectName[] onames = getObjNames(mbs, pattern, dmhdlr, parentobjname, hasNoParentOnly, exp);
    onames = dmhdlr.trim(onames, domain, relativeStringRep);
    return onames;
  }
  
  /**
   * The type passed here is fully qualified already
   */
  public static ObjectName[] getObjNames
    (Object mbs, ObjectName pattern, WLJMXHandler dmhdlr, 
     ObjectName parentobjname, boolean hasNoParentOnly, QueryExp exp) 
    throws Exception {
    ObjectName oname = pattern;
    Set s = mbs_queryNames(mbs, oname, exp);
    //System.out.println("Initially returned set with num items: " + s.size() + " ... " + s);
    //System.out.println("Initially returned set with num items: " + s.size());
    //System.out.println(s);
    Set s2 = new HashSet();
    //Hashtable ht = new Hashtable();
    //QueryExp exp = getQueryExp(type, name, ht);
    for(Iterator itr = s.iterator(); itr.hasNext(); ) {
      ObjectName wlobjname = (ObjectName)itr.next();
      boolean addme = false;
      //System.out.println("wlobjname: " + wlobjname.getClass().getName() + " ==> " + wlobjname + " ==> " + wlobjname.getCanonicalName());
      ObjectName wlobjpar = dmhdlr.getParent(wlobjname, mbs);
      if(hasNoParentOnly) {
        if(wlobjpar == null) {
          //System.out.println("adding because wlobjpar=null and hasNoParentOnly=true");
          addme = true;
        }
      } else {
        if(parentobjname == null) {
          //System.out.println("adding because parentobjname=null and hasNoParentOnly=false");
          addme = true;
        } else {
          if(equals(wlobjpar, parentobjname)) {
            //System.out.println("adding because parentobjname!=null and hasNoParentOnly=false and equalsparent");
            addme = true;
          }
        }
      }
      if(addme) {
        s2.add(wlobjname);
      }
    }
    //System.out.println("object names returned: " + s2);
    return (ObjectName[])s2.toArray(new ObjectName[0]);
  }

  public static String[] getDomains(Object mbs) throws Exception {    
    //String[] domains = mbs.getDomains();
    ObjectName pattern = new ObjectName("*:*");
    Set s = mbs_queryNames(mbs, pattern, null);
    Set s2 = new HashSet();
    for(Iterator itr = s.iterator(); itr.hasNext(); ) {
      s2.add(((ObjectName)itr.next()).getDomain());
    }
    String[] domains = (String[])s2.toArray(new String[0]);
    return domains;
  }
  
  public static ObjectName getObjNameForStringRep
    (Object mbs, WLJMXHandler dmhdlr, 
     ObjectName cmo, String domain, String dir) throws Exception {
    boolean firstnodeisroot = false;
    if(dir.startsWith("/")) {
      firstnodeisroot = true;
      cmo = null;
    }
    String[] dirs = StringUtils.split(dir, "/");
    boolean firstnodeiter = true;
    for(int i = 0; i < dirs.length; i++) {
      if(dirs[i].equals("")) {
        continue;
      } 
      if(dirs[i].equals("..")) {
        cmo = dmhdlr.getParent(cmo, mbs);
      } else {
        //ObjectName pattern = dmhdlr.getLookupPattern(domain, dirs[i]);
        if(firstnodeiter && firstnodeisroot) {
          cmo = getSingleObjName(mbs, domain, dirs[i], dmhdlr, null, true, null);
        } else {
          cmo = getSingleObjName(mbs, domain, dirs[i], dmhdlr, cmo, false, null);
        }
      }
      firstnodeiter = false;
    }
    return cmo;
  }
  
  public static ObjectName[] getSubsetForMatch(ObjectName[] wlms, Map matchMap) 
    throws Exception {
    if(matchMap == null) {
      return wlms;
    }
    List al = new ArrayList();
    for(int i = 0; i < wlms.length; i++) {
      boolean ismatch = true;
      for(Iterator itr = matchMap.keySet().iterator(); itr.hasNext(); ) {
        String key = (String)itr.next();
        String value = (String)matchMap.get(key);
        String pp = wlms[i].getKeyProperty(key);
        //System.out.println(wlms[i] + "::" + key + "::" + value + "::" + pp);
        if(pp == null || (!(StringUtils.matchSimpleRegex(pp, value)))) {
          ismatch = false;
          break;
        }
      }
      if(ismatch) {
        al.add(wlms[i]);
      }
    }
    return (ObjectName[])al.toArray(new ObjectName[0]);
  }
  
  /**
   * Takes a fully qualified type (e.g. ServerConfig)
   */
  public static ObjectName getSingleObjName(ObjectName[] wlms) 
    throws Exception {
    if(wlms.length == 0) {
      throw new FacadeBenignException("No ObjNames returned for query");
      //return null;
    } else if(wlms.length == 1) {
      return wlms[0];
    } else {
      throw new FacadeBenignException("Multiple ObjNames returned for query");
    }
  }
  
  public static ObjectName getObjName(WLFacadeContextHelper wlctx, String stringrep) throws Exception {
    WLScriptHelper.ensureMBSTree(wlctx);
    WLMBSTree wltree = (WLMBSTree)wlctx.getCurrentTree();
    
    ObjectName cmo = (ObjectName)wltree.pointer();
    String[] splitstr = twoElemSplit(stringrep, ":");
    String domain = wltree.getDomain();
    if(splitstr[1] != null) {
      domain = splitstr[0];
      stringrep = splitstr[1];
      cmo = null;
    }
    WLJMXHandler dmhdlr = wltree.getJMXHandler();
    cmo = getObjNameForStringRep(wltree.getMBS(), dmhdlr, cmo, domain, stringrep);
    return cmo;
  }
  
  public static Object tombean(WLFacadeContextHelper wlctx, String stringrep) throws Exception {
    ObjectName cmo = getObjName(wlctx, stringrep);
    return tombean(wlctx, cmo);
  }
  
  public static Object tombean(WLFacadeContextHelper wlctx, ObjectName cmo) throws Exception {
    WLScriptHelper.ensureMBSTree(wlctx);
    WLMBSTree wltree = (WLMBSTree)wlctx.getCurrentTree();
    Object mbs = wltree.getMBS();
    Object rtn = null;
    Throwable exc1 = null;
    Throwable exc2 = null;
    Throwable exc3 = null;
    
    if(mbs instanceof RemoteMBeanServer) {
      //pre WLS 9.0
      try {
        MBeanHome mbh = ((RemoteMBeanServer)mbs).getMBeanHome();
        rtn = mbh.getMBean(cmo);
      } catch(Throwable thr) { 
        exc1 = thr;
      }
      if(rtn == null) {
        try {
          rtn = getCommoMBean(wlctx.getConnection().getCommoMBS(), cmo);
        } catch(Throwable exc) { //since  CommoMBeanServer is not on 90 codebase
          exc2 = exc;
          //System.err.println("Error finding MBean from MBeanHome: " + exc);
        }
      }
    } else if(mbs instanceof MBeanServerConnection) {
      //WLS 9.0 and beyond
      MBeanServerConnection mbsconn = (MBeanServerConnection)mbs;
      MBeanInfo info = mbsconn.getMBeanInfo(cmo);
      Class intfclass = null;
      try {
        intfclass = Class.forName(info.getClassName());
      } catch(Throwable thr) {  
        exc1 = thr;
      } 
      try {
        if(intfclass == null || !intfclass.isInterface()) {
          if(info instanceof ModelMBeanInfo ) {
            intfclass = Class.forName((String)(((ModelMBeanInfo)info).getMBeanDescriptor()).getFieldValue("interfaceclassname"));
          }
        }
      } catch(Throwable thr) {  
        exc2 = thr;
      } 
      try {
        rtn = MBeanServerInvocationHandler.newProxyInstance(mbsconn, cmo, intfclass, true);
      } catch(Throwable thr) {  
        exc3 = thr;
      } 
    }
    
    //if(rtn != null) {
    //System.out.println("rtn: " + rtn.getClass().getName() + " ... " + rtn);
    //}
    if(rtn == null) {
      throw new FacadeBenignException("Could not find MBean. Exceptions: (" + exc1 + ") (" + exc2 + ") (" + exc3 + ")");
    }
    return rtn;
  }

  public static boolean equals(ObjectName o1, ObjectName o2) {
    // System.out.println("objNamesEquals rtn: " + o1 + "  " + rtn);
    if(o1 == null && o2 == null) {
      return true;
    } else if(o1 != null && o2 != null) {
      return o1.equals(o2);
    } else {
      return false;
    }
  }

  public static String getClassNameWithoutPackage(Class c) throws Exception {
    Package p = c.getPackage();
    String pkgname = "";
    String classonlyname = c.getName();
    if(p != null) {
      pkgname = p.getName();
      classonlyname = classonlyname.substring(pkgname.length() + 1);
    }
    return classonlyname;
  }

  public static void ensureConnected(WLFacadeContextHelper wlctx) throws Exception {
    if(!wlctx.getFacadeContext().isFlagSet(WLFacadeContextHelper.CONNECTED)) {
      String errmsg = "You are not connected. Please connect first";
      throw new FacadeBenignException(errmsg);
    }
  }

  public static void ensureAdminServer(WLFacadeContextHelper wlctx) throws Exception {
    if(!wlctx.getFacadeContext().isFlagSet(WLFacadeContextHelper.ADMINSERVER)) {
      String errmsg = "You are not on an administration server";
      throw new FacadeBenignException(errmsg);
    }
  }
  
  public static void ensureNotDomainRoot(WLFacadeContextHelper wlctx) throws Exception {
    if(wlctx.getFacadeContext().isFlagSet(WLFacadeContextHelper.DOMAINROOT)) {
      String errmsg = "You are at the domain root - function not available";
      throw new FacadeBenignException(errmsg);
    }
  }
  
  public static void ensurePrimaryDomain(WLFacadeContextHelper wlctx) throws Exception {
    if(!wlctx.getFacadeContext().isFlagSet(WLFacadeContextHelper.PRIMARYDOMAIN)) {
      String errmsg = "This is only available on the Primary Domain";
      throw new FacadeBenignException(errmsg);
    }
  }
    
  public static void ensureMBSTree(WLFacadeContextHelper wlctx) throws Exception {
    WLTree wltree = wlctx.getCurrentTree();
    if(wltree == null || !(wltree instanceof WLMBSTree)) {
      String errmsg = "You are not navigating a tree mapped to an mbean server";
      throw new FacadeBenignException(errmsg);
    }
  }
  
  public static void ensureTreeSelected(WLFacadeContextHelper wlctx) throws Exception {
    WLTree wltree = wlctx.getCurrentTree();
    if(wltree == null) {
      String errmsg = "You are not navigating any tree at this point";
      throw new FacadeBenignException(errmsg);
    }
  }
  
  public static String[] twoElemSplit(String stringrep, String sep) {
    String[] rs = new String[2];
    int indexOfSep = stringrep.indexOf(sep);
    if(indexOfSep == -1) {
      rs[0] = stringrep;
    } else {
      rs[0] = stringrep.substring(0, indexOfSep);
      rs[1] = stringrep.substring(indexOfSep + sep.length());
    }
    return rs;
  }

  public static Map toMap(Object obj) throws Exception {
    Map m = null;
    if(obj == null) {
      m = new HashMap();
    } else if(obj instanceof InputStream) {
      Properties p = new Properties();
      p.load((InputStream)obj);
      m = p;
    } else if(obj instanceof File) {
      FileInputStream fis = new FileInputStream((File)obj);
      m = toMap(fis);
      fis.close();
    } else if(obj instanceof String) {
      //treat as filename
      m = toMap(new File((String)obj));
    } else {
      throw new FacadeBenignException("To convert to Map, parameter must be one of: null, InputStream, File, String(filename)");
    }
    return m;
  }

  public static Set mbs_queryNames(Object mbs, ObjectName oname, QueryExp exp) throws Exception {
    Set s = null;
    if(mbs instanceof MBeanServer) {
      s = ((MBeanServer)mbs).queryNames(oname, exp);
    } else if(mbs instanceof MBeanServerConnection) {
      s = ((MBeanServerConnection)mbs).queryNames(oname, exp);
    }
    return s;
  }

  public static void mbs_setAttribute(Object mbs, ObjectName woname, Attribute att) throws Exception {
    if(mbs instanceof MBeanServerConnection) {
      ((MBeanServerConnection)mbs).setAttribute(woname, att);
    } else {
      ((MBeanServer)mbs).setAttribute(woname, att);
    }
  }
  
  public static Object mbs_getAttribute(Object mbs, ObjectName woname, String att) throws Exception {
    Object s = null;
    if(mbs instanceof MBeanServerConnection) {
      s = ((MBeanServerConnection)mbs).getAttribute(woname, att);
    } else {
      s = ((MBeanServer)mbs).getAttribute(woname, att);
    }
    return s;
  }
  
  public static Object mbs_invoke(Object mbs, ObjectName cmo, String opname, Object[] args) throws Exception {
    String[] signature = new String[args.length];
    for(int i = 0; i < signature.length; i++) {
      signature[i] = args[i].getClass().getName();
    }
    return mbs_invoke(mbs, cmo, opname, args, signature);
  }

  public static Object mbs_invoke(Object mbs, ObjectName cmo, String opname, Object[] args, String[] signature) throws Exception {
    Object obj = null;
    if(mbs instanceof MBeanServer) {
      obj = ((MBeanServer)mbs).invoke(cmo, opname, args, signature);
    } else if(mbs instanceof MBeanServerConnection) {
      obj = ((MBeanServerConnection)mbs).invoke(cmo, opname, args, signature);
    }
    return obj;
  }

  public static MBeanInfo mbs_getMBeanInfo(Object mbs, ObjectName cmo) throws Exception {
    MBeanInfo mbinfo = null;
    if(mbs instanceof MBeanServer) {
      mbinfo = ((MBeanServer)mbs).getMBeanInfo(cmo);
    } else if(mbs instanceof MBeanServerConnection) {
      mbinfo = ((MBeanServerConnection)mbs).getMBeanInfo(cmo);
    }
    return mbinfo;
  }

  public static String mbs_getDefaultDomain(Object mbs) throws Exception {
    String s = null;
    if(mbs instanceof MBeanServer) {
      s = ((MBeanServer)mbs).getDefaultDomain();
    } else if(mbs instanceof MBeanServerConnection) {
      s = ((MBeanServerConnection)mbs).getDefaultDomain();
    }
    return s;
  }

  public static AttributeList mbs_getAttributes(Object mbs, ObjectName cmo, String[] attnames) throws Exception {
    AttributeList s = null;
    if(mbs instanceof MBeanServer) {
      s = ((MBeanServer)mbs).getAttributes(cmo, attnames);
    } else if(mbs instanceof MBeanServerConnection) {
      s = ((MBeanServerConnection)mbs).getAttributes(cmo, attnames);
    }
    return s;
  }
  
  public static MBeanServerConnection getMBSConn(String svcUrlStr, Hashtable env) throws Throwable {
    JMXServiceURL serviceURL = new JMXServiceURL(svcUrlStr);
    MBeanServerConnection mbs = JMXConnectorFactory.connect(serviceURL, env).getMBeanServerConnection();;
    return mbs;
  }
  
  /**
   * This actually returns an instance of CommoMBeanServer.
   * We don't use the type in the code, because WLS 9.0 does not support it.
   */
  public static Object getCommoMBS(String serverName, String url, String username, String password) 
    throws Exception {
    Method m = Class.forName("weblogic.management.commo.Config").
      getMethod("getMBeanServer", new Class[]{String.class, String.class, String.class, String.class});
    return m.invoke(null, new Object[]{serverName, url, username, password});
    //CommoMBeanServer commombs = weblogic.management.commo.Config.getMBeanServer
    //  (serverName, url, username, password);
    //return commombs;
  }
   
  public static Object getCommoMBean(Object commoMBS, ObjectName key) throws Exception {
    //return ((CommoMBeanServer)commoMBS).getMBean(cmo);
    Method m = Class.forName("weblogic.management.commo.CommoMBeanServer").
      getMethod("getMBean", new Class[]{ObjectName.class} );
    return m.invoke(commoMBS, new ObjectName[]{key});
  }
    
}

